// Cjc.AwesomiumWrapper.h

#pragma once
#include <stdlib.h>
#include <string.h>
#include <msclr\marshal.h>
#include <msclr\marshal_cppstd.h>
#include <msclr\marshal_windows.h>
#include <cliext/adapter> 
#include <cliext/map>
#include "WebCore.h"

using namespace System;
using namespace System::Collections::Generic;
using namespace System::Collections::ObjectModel;
using namespace msclr::interop;

namespace CjcAwesomiumWrapper
{
	typedef void (*BeginNavigationHandler)( const std::string& url, const std::wstring& frameName );
	typedef void (*BeginLoadingHandler)( const std::string& url, const std::wstring& frameName, int statusCode, const std::wstring& mimeType );
	typedef void (*FinishLoadingHandler)();
	typedef void (*CallbackHandler)( const std::wstring& objectName, const std::wstring& callbackName, const Awesomium::JSArguments& args );
	typedef void (*ReceiveTitleHandler)( const std::wstring& title, const std::wstring& frameName );
	typedef void (*ChangeTooltipHandler)( const std::wstring& tooltip );
	typedef void (*ChangeCursorHandler)( Awesomium::CursorType cursor );
	typedef void (*ChangeKeyboardFocusHandler)( bool isFocused );
	typedef void (*ChangeTargetURLHandler)( const std::string& url );
	typedef void (*OpenExternalLinkHandler)( const std::string& url, const std::wstring& source );
	typedef void (*WebViewCrashedHandler)();
	typedef void (*PluginCrashedHandler)( const std::wstring& pluginName );
	typedef void (*RequestMoveHandler)( int x, int y );
	typedef void (*GetPageContentsHandler)( const std::string& url, const std::wstring& contents );
	typedef void (*DOMReadyHandler)();

	public ref class JSValue
	{
		private:

			const Awesomium::JSValue* value;

		public:

			JSValue() { this->value = new Awesomium::JSValue(); }
			JSValue( const Awesomium::JSValue& value ) { this->value = &value; }
			JSValue( bool value ) { this->value = new Awesomium::JSValue( value ); }
			JSValue( int value ) { this->value = new Awesomium::JSValue( value ); }
			JSValue( double value ) { this->value = new Awesomium::JSValue( value ); }
			JSValue( String^ value ) { this->value = new Awesomium::JSValue( marshal_as<std::wstring>( value ) ); }

			virtual ~JSValue() { delete value; value = NULL; }

			bool IsBoolean() { return value->isBoolean(); }
			bool IsInteger() { return value->isInteger(); }
			bool IsDouble() { return value->isDouble(); }
			bool IsNumber() { return value->isNumber(); }
			bool IsString() { return value->isString(); }
			bool IsNull() { return value->isNull(); }
			bool IsArray() { return value->isArray(); }
			bool IsObject() { return value->isObject(); }

			operator Object^() { return Value(); }
			operator String^() { return IsNull() ? nullptr : System::Convert::ToString( Value() ); }
			int ToInteger() { return value->toInteger(); }
			double ToDouble() { return value->toDouble(); }
			bool ToBoolean() { return value->toBoolean(); }
			virtual String^ ToString() override { return (String^)this; }

			Collection<JSValue^>^ GetArray()
			{
				if ( !IsArray() ) return nullptr;

				Awesomium::JSValue::Array jsArray = value->getArray();
				Collection<JSValue^>^ coll = gcnew Collection<JSValue^>();

				for each ( Awesomium::JSValue value in jsArray )
				{
					coll->Add( gcnew JSValue( (const Awesomium::JSValue&)*new Awesomium::JSValue( value ) ) );
				}

				return coll;
			}

			Dictionary<String^, JSValue^>^ GetObject()
			{
				if ( !IsObject() ) return nullptr;

				Awesomium::JSValue::Object obj = value->getObject();
				Dictionary<String^, JSValue^>^ dict = gcnew Dictionary<String^, JSValue^>( obj.size() );

				for each( std::pair<std::wstring, Awesomium::JSValue> value in obj )
				{
					dict->Add( marshal_as<String^>( value.first ), gcnew JSValue( (const Awesomium::JSValue&)*new Awesomium::JSValue( value.second ) ) );
				}

				return dict;
			}

			System::Object^ Value()
			{
				if ( IsBoolean() ) return ToBoolean();
				if ( IsInteger() ) return ToInteger();
				if ( IsDouble() || IsNumber() ) return ToDouble();
				if ( IsArray() ) return GetArray();
				if ( IsObject() ) return GetObject();
				if ( IsString() ) return marshal_as<String^>( value->toString() );
				return nullptr;
			}

		internal:

			operator Awesomium::JSValue() { return *value; }
	};
/*
	/// <summary>
	/// JSValue is a class that represents a JavaScript value.
	/// It can be initialized from and converted to several types:
	/// boolean, integer, double, string
	/// </summary>

	public ref class JSValue
	{
	private:
		Object^ value;

	internal:
		JSValue::JSValue(const Awesomium::JSValue& nativeValue);
		Awesomium::JSValue ToNative();

	public:
		/// <summary>Creates a null JSValue.</summary>
		JSValue();

		/// <summary>Creates a JSValue initialized with a boolean.</summary>
		JSValue(bool value);

		/// <summary>Creates a JSValue initialized with an integer.</summary>
		JSValue(int value);

		/// <summary>Creates a JSValue initialized with a double.</summary>
		JSValue(double value);

		/// <summary>Creates a JSValue initialized with a string.</summary>
		JSValue(System::String^ value);

		/// <summary>Creates a JSValue initialized with an array.</summary>
		JSValue(Collection<JSValue^>^ value);

		/// <summary>Creates a JSValue initialized with an object.</summary>
		JSValue(Dictionary<String^, JSValue^>^ value);

		/// <summary>Whether or not this JSValue is a boolean.</summary>
		property bool IsBoolean { bool get(); }

		/// <summary>Returns whether or not this JSValue is an integer.</summary>
		property bool IsInteger { bool get(); }

		/// <summary>Returns whether or not this JSValue is a double.</summary>
		property bool IsDouble { bool get(); }

		/// <summary>Returns whether or not this JSValue is a string.</summary>
		property bool IsString { bool get(); }

		/// <summary>Returns whether or not this JSValue is null.</summary>
		property bool IsNull { bool get(); }

		/// <summary>Returns whether or not this JSValue is an array.</summary>
		property bool IsArray { bool get(); }

		/// <summary>Returns whether or not this JSValue is an object.</summary>
		property bool IsObject { bool get(); }

		/// <summary>Returns the value as an object.</summary>
		property Object^ Value { Object^ get(); }

		/// <summary>Returns this JSValue as a string (converting if necessary).</summary>
		virtual String^ ToString() override;

		/// <summary>Returns this JSValue as a boolean.</summary>
		bool ToBoolean();

		/// <summary>Returns this JSValue as a double.</summary>
		double ToDouble();

		/// <summary>Returns this JSValue as an integer.</summary>
		int ToInteger();

		/// <summary>Gets this JSValue's array value</summary>
		Collection<JSValue^>^ GetArray();

		/// <summary>Gets this JSValue's object value</summary>
		Dictionary<String^, JSValue^>^ GetObject();
	};
*/

	/// <summary>
	/// FutureJSValue is a special wrapper around a JSValue that allows
	/// asynchronous retrieval of the actual value at a later time.
	/// If you are unfamiliar with the concept of a 'Future', please see:
	/// http://en.wikipedia.org/wiki/Futures_and_promises
	/// </summary>

	public ref class FutureJSValue
	{
	private:
		Awesomium::FutureJSValue* futureJSValue;
	
	internal:
		FutureJSValue::FutureJSValue(const Awesomium::FutureJSValue& futureJSValue)
			: futureJSValue(new Awesomium::FutureJSValue(futureJSValue))
		{
		} 
	
	public:
		~FutureJSValue()
		{
			FutureJSValue::!FutureJSValue();
		}

		/// <summary>
		/// If the internal JSValue has been computed, immediately returns
		/// the value, else, blocks the calling thread until it has.
		/// </summary>
		JSValue^ Get()
		{
			return gcnew JSValue( futureJSValue->get() );
		}

		/// <summary>
		/// If the internal JSValue has been computed, immediately returns
		/// the value, else, blocks the calling thread until it has.
		/// </summary>
		JSValue^ GetWithTimeout( int timeoutMs )
		{
			return gcnew JSValue( futureJSValue->getWithTimeout( timeoutMs ) );
		}

	protected:
		!FutureJSValue()
		{
			delete futureJSValue;
		}
	};

	ref class WebViewListener;

	class DelegatingWebViewListener : public Awesomium::WebViewListener
	{
		public:

			DelegatingWebViewListener()
			{
				BeginLoading = 0;
				BeginNavigation = 0;
				FinishLoading = 0;
				Callback = 0;
				ReceiveTitle = 0;
				ChangeTooltip = 0;
				ChangeCursor = 0;
				ChangeKeyboardFocus = 0;
				ChangeTargetURL = 0;
				OpenExternalLink = 0;
			}

			virtual ~DelegatingWebViewListener() {}

			void onBeginNavigation( Awesomium::WebView* caller, const std::string& url, const std::wstring& frameName )
			{
				if ( BeginNavigation ) (*BeginNavigation)( url, frameName );
			}

			BeginNavigationHandler BeginNavigation;

			void onBeginLoading( Awesomium::WebView* caller, const std::string& url, const std::wstring& frameName, int statusCode, const std::wstring& mimeType )
			{
				if ( BeginLoading ) (*BeginLoading)( url, frameName, statusCode, mimeType );
			}

			BeginLoadingHandler BeginLoading;

			void onFinishLoading( Awesomium::WebView* caller )
			{
				if ( FinishLoading ) (*FinishLoading)();
			}

			FinishLoadingHandler FinishLoading;

			void onCallback( Awesomium::WebView* caller, const std::wstring& objectName, const std::wstring& callbackName, const Awesomium::JSArguments& args )
			{
				if ( Callback ) (*Callback)( objectName, callbackName, args );
			}

			CallbackHandler Callback;

			void onReceiveTitle( Awesomium::WebView* caller, const std::wstring& title, const std::wstring& frameName )
			{
				if ( ReceiveTitle ) (*ReceiveTitle)( title, frameName );
			}

			ReceiveTitleHandler ReceiveTitle;

			void onChangeTooltip( Awesomium::WebView* caller, const std::wstring& tooltip )
			{
				if ( ChangeTooltip ) (*ChangeTooltip)( tooltip );
			}

			ChangeTooltipHandler ChangeTooltip;

#if defined(_WIN32)

			void onChangeCursor( Awesomium::WebView* caller, Awesomium::CursorType cursor )
			{
				if ( ChangeCursor ) (*ChangeCursor)( cursor );
			}

			ChangeCursorHandler ChangeCursor;

#endif

			void onChangeKeyboardFocus( Awesomium::WebView* caller, bool isFocused )
			{
				if ( ChangeKeyboardFocus ) (*ChangeKeyboardFocus)( isFocused );
			}

			ChangeKeyboardFocusHandler ChangeKeyboardFocus;

			void onChangeTargetURL( Awesomium::WebView* caller, const std::string& url )
			{
				if ( ChangeTargetURL ) (*ChangeTargetURL)( url );
			}

			ChangeTargetURLHandler ChangeTargetURL;

			void onOpenExternalLink( Awesomium::WebView* caller, const std::string& url, const std::wstring& source )
			{
				if ( OpenExternalLink ) (*OpenExternalLink)( url, source );
			}

			OpenExternalLinkHandler OpenExternalLink;

			void onWebViewCrashed( Awesomium::WebView* caller )
			{
				if ( WebViewCrashed ) (*WebViewCrashed)();
			}

			WebViewCrashedHandler WebViewCrashed;

			void onPluginCrashed( Awesomium::WebView* caller, const std::wstring& pluginName )
			{
				if ( PluginCrashed ) (*PluginCrashed)( pluginName );
			}

			PluginCrashedHandler PluginCrashed;

			void onRequestMove( Awesomium::WebView* caller, int x, int y )
			{
				if ( RequestMove ) (*RequestMove)( x, y );
			}

			RequestMoveHandler RequestMove;

			void onGetPageContents( Awesomium::WebView* caller, const std::string& url, const std::wstring& contents )
			{
				if ( GetPageContents ) (*GetPageContents)( url, contents );
			}

			GetPageContentsHandler GetPageContents;

			void onDOMReady( Awesomium::WebView* caller )
			{
				if ( DOMReady ) (*DOMReady)();
			}

			DOMReadyHandler DOMReady;
	};

	public enum class CursorType
	{
		CursorPointer,
		CursorCross,
		CursorHand,
		CursorIBeam,
		CursorWait,
		CursorHelp,
		CursorEastResize,
		CursorNorthResize,
		CursorNorthEastResize,
		CursorNorthWestResize,
		CursorSouthResize,
		CursorSouthEastResize,
		CursorSouthWestResize,
		CursorWestResize,
		CursorNorthSouthResize,
		CursorEastWestResize,
		CursorNorthEastSouthWestResize,
		CursorNorthWestSouthEastResize,
		CursorColumnResize,
		CursorRowResize,
		CursorMiddlePanning,
		CursorEastPanning,
		CursorNorthPanning,
		CursorNorthEastPanning,
		CursorNorthWestPanning,
		CursorSouthPanning,
		CursorSouthEastPanning,
		CursorSouthWestPanning,
		CursorWestPanning,
		CursorMove,
		CursorVerticalText,
		CursorCell,
		CursorContextMenu,
		CursorAlias,
		CursorProgress,
		CursorNoDrop,
		CursorCopy,
		CursorNone,
		CursorNotAllowed,
		CursorZoomIn,
		CursorZoomOut,
		CursorCustom
	};

	public ref class WebViewListener
	{
		public:

		private:

			DelegatingWebViewListener* webViewListener;

			void OnBeginNavigation( const std::string& url, const std::wstring& frameName )
			{
				BeginNavigation( marshal_as<String^>( url ), marshal_as<String^>( frameName ) );
			};

			void OnBeginLoading( const std::string& url, const std::wstring& frameName, int statusCode, const std::wstring& mimeType )
			{
				BeginLoading( marshal_as<String^>( url ), marshal_as<String^>( frameName ), statusCode, marshal_as<String^>( mimeType ) );
			};

			void OnFinishLoading()
			{
				FinishLoading();
			};

			void OnCallback( const std::wstring& objectName, const std::wstring& callbackName, const Awesomium::JSArguments& args )
			{
				array<JSValue^>^ argArray = gcnew array<JSValue^>( args.size() );

				for ( UINT i = 0; i < args.size(); ++ i ) argArray[ i ] = gcnew JSValue( args[ i ] );

				Callback( marshal_as<String^>( objectName ), marshal_as<String^>( callbackName ), argArray );
			};

			void OnReceiveTitle( const std::wstring& title, const std::wstring& frameName )
			{
				ReceiveTitle( marshal_as<String^>( title ), marshal_as<String^>( frameName ) );
			};

			void OnChangeTooltip( const std::wstring& tooltip )
			{
				ChangeTooltip( marshal_as<String^>( tooltip ) );
			};

			void OnChangeCursor( Awesomium::CursorType cursor )
			{
				ChangeCursor( (CursorType)cursor );
			};

			void OnChangeKeyboardFocus( bool isFocused )
			{
				ChangeKeyboardFocus( isFocused );
			};

			void OnChangeTargetURL( const std::string& url )
			{
				ChangeTargetURL( marshal_as<String^>( url ) );
			};

			void OnOpenExternalLink( const std::string& url, const std::wstring& source )
			{
				OpenExternalLink( marshal_as<String^>( url ), marshal_as<String^>( source ) );
			};

			void OnWebViewCrashed()
			{
				WebViewCrashed();
			};

			void OnPluginCrashed( const std::wstring& pluginName )
			{
				PluginCrashed( marshal_as<String^>( pluginName ) );
			};

			void OnRequestMove( int x, int y )
			{
				RequestMove( x, y );
			};

			void OnGetPageContents( const std::string& url, const std::wstring& contents )
			{
				GetPageContents( marshal_as<String^>( url ), marshal_as<String^>( contents ) );
			};

			void OnDOMReady()
			{
				DOMReady();
			};

			delegate void BeginNavigationInternal( const std::string& url, const std::wstring& frameName );
			delegate void BeginLoadingInternal( const std::string& url, const std::wstring& frameName, int statusCode, const std::wstring& mimeType );
			delegate void FinishLoadingInternal();
			delegate void CallbackInternal( const std::wstring& objectName, const std::wstring& callbackName, const Awesomium::JSArguments& args );
			delegate void ReceiveTitleInternal( const std::wstring& title, const std::wstring& frameName );
			delegate void ChangeTooltipInternal( const std::wstring& tooltip );
			delegate void ChangeCursorInternal( Awesomium::CursorType cursor );
			delegate void ChangeKeyboardFocusInternal( bool isFocused );
			delegate void ChangeTargetURLInternal( const std::string& url );
			delegate void OpenExternalLinkInternal( const std::string& url, const std::wstring& source );
			delegate void WebViewCrashedInternal();
			delegate void PluginCrashedInternal( const std::wstring& pluginName );
			delegate void RequestMoveInternal( int x, int y );
			delegate void GetPageContentsInternal( const std::string& url, const std::wstring& contents );
			delegate void DOMReadyInternal();

			// Keep delegates alive
			BeginNavigationInternal^ beginNavigationInternal;
			BeginLoadingInternal^ beginLoadingInternal;
			FinishLoadingInternal^ finishLoadingInternal;
			CallbackInternal^ callbackInternal;
			ReceiveTitleInternal^ receiveTitleInternal;
			ChangeTooltipInternal^ changeTooltipInternal;
			ChangeCursorInternal^ changeCursorInternal;
			ChangeKeyboardFocusInternal^ changeKeyboardFocusInternal;
			ChangeTargetURLInternal^ changeTargetURLInternal;
			OpenExternalLinkInternal^ openExternalLinkInternal;
			WebViewCrashedInternal^ webViewCrashedInternal;
			PluginCrashedInternal^ pluginCrashedInternal;
			RequestMoveInternal^ requestMoveInternal;
			GetPageContentsInternal^ getPageContentsInternal;
			DOMReadyInternal^ domReadyInternal;

		public:

			WebViewListener()
			{
				this->webViewListener = new DelegatingWebViewListener();

				this->webViewListener->BeginNavigation = (BeginNavigationHandler)System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(
					beginNavigationInternal = gcnew BeginNavigationInternal( this, &WebViewListener::OnBeginNavigation ) ).ToPointer();

				this->webViewListener->BeginLoading = (BeginLoadingHandler)System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(
					beginLoadingInternal = gcnew BeginLoadingInternal( this, &WebViewListener::OnBeginLoading ) ).ToPointer();

				this->webViewListener->FinishLoading = (FinishLoadingHandler)System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(
					finishLoadingInternal = gcnew FinishLoadingInternal( this, &WebViewListener::OnFinishLoading ) ).ToPointer();

				this->webViewListener->Callback = (CallbackHandler)System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(
					callbackInternal = gcnew CallbackInternal( this, &WebViewListener::OnCallback ) ).ToPointer();

				this->webViewListener->ReceiveTitle = (ReceiveTitleHandler)System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(
					receiveTitleInternal = gcnew ReceiveTitleInternal( this, &WebViewListener::OnReceiveTitle ) ).ToPointer();

				this->webViewListener->ChangeTooltip = (ChangeTooltipHandler)System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(
					changeTooltipInternal = gcnew ChangeTooltipInternal( this, &WebViewListener::OnChangeTooltip ) ).ToPointer();

				this->webViewListener->ChangeCursor = (ChangeCursorHandler)System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(
					changeCursorInternal = gcnew ChangeCursorInternal( this, &WebViewListener::OnChangeCursor ) ).ToPointer();

				this->webViewListener->ChangeKeyboardFocus = (ChangeKeyboardFocusHandler)System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(
					changeKeyboardFocusInternal = gcnew ChangeKeyboardFocusInternal( this, &WebViewListener::OnChangeKeyboardFocus ) ).ToPointer();

				this->webViewListener->ChangeTargetURL = (ChangeTargetURLHandler)System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(
					changeTargetURLInternal = gcnew ChangeTargetURLInternal( this, &WebViewListener::OnChangeTargetURL ) ).ToPointer();

				this->webViewListener->OpenExternalLink = (OpenExternalLinkHandler)System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(
					openExternalLinkInternal = gcnew OpenExternalLinkInternal( this, &WebViewListener::OnOpenExternalLink ) ).ToPointer();

				this->webViewListener->WebViewCrashed = (WebViewCrashedHandler)System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(
					webViewCrashedInternal = gcnew WebViewCrashedInternal( this, &WebViewListener::OnWebViewCrashed ) ).ToPointer();

				this->webViewListener->PluginCrashed = (PluginCrashedHandler)System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(
					pluginCrashedInternal = gcnew PluginCrashedInternal( this, &WebViewListener::OnPluginCrashed ) ).ToPointer();

				this->webViewListener->RequestMove = (RequestMoveHandler)System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(
					requestMoveInternal = gcnew RequestMoveInternal( this, &WebViewListener::OnRequestMove ) ).ToPointer();

				this->webViewListener->GetPageContents = (GetPageContentsHandler)System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(
					getPageContentsInternal = gcnew GetPageContentsInternal( this, &WebViewListener::OnGetPageContents ) ).ToPointer();

				this->webViewListener->DOMReady = (DOMReadyHandler)System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(
					domReadyInternal = gcnew DOMReadyInternal( this, &WebViewListener::OnDOMReady ) ).ToPointer();
			}

			virtual ~WebViewListener() { delete webViewListener; webViewListener = NULL; }

			delegate void BeginNavigationDelegate( String^ url, String^ frameName );
			event BeginNavigationDelegate^ BeginNavigation;

			delegate void BeginLoadingDelegate( String^ url, String^ frameName, int statusCode, String^ mimeType );
			event BeginLoadingDelegate^ BeginLoading;

			delegate void FinishLoadingDelegate();
			event FinishLoadingDelegate^ FinishLoading;

			delegate void CallbackDelegate( String^ objectName, String^ callbackName, array<JSValue^>^ args );
			event CallbackDelegate^ Callback;

			delegate void ReceiveTitleDelegate( String^ title, String^ frameName );
			event ReceiveTitleDelegate^ ReceiveTitle;

			delegate void ChangeTooltipDelegate( String^ tooltip );
			event ChangeTooltipDelegate^ ChangeTooltip;

			delegate void ChangeCursorDelegate( CursorType cursor );
			event ChangeCursorDelegate^ ChangeCursor;

			delegate void ChangeKeyboardFocusDelegate( bool isFocused );
			event ChangeKeyboardFocusDelegate^ ChangeKeyboardFocus;

			delegate void ChangeTargetURLDelegate( String^ url );
			event ChangeTargetURLDelegate^ ChangeTargetURL;

			delegate void OpenExternalLinkDelegate( String^ url, String^ source );
			event OpenExternalLinkDelegate^ OpenExternalLink;

			delegate void WebViewCrashedDelegate();
			event WebViewCrashedDelegate^ WebViewCrashed;

			delegate void PluginCrashedDelegate( String^ pluginName );
			event PluginCrashedDelegate^ PluginCrashed;

			delegate void RequestMoveDelegate( int x, int y );
			event RequestMoveDelegate^ RequestMove;

			delegate void GetPageContentsDelegate( String^ url, String^ contents );
			event GetPageContentsDelegate^ GetPageContents;

			delegate void DOMReadyDelegate();
			event DOMReadyDelegate^ DOMReady;

		internal:

			operator DelegatingWebViewListener*() { return webViewListener; }
	};

	public ref struct Rect
	{
		public:
			int X;
			int Y;
			int Width;
			int Height;
	};

	public enum class MouseButton { Left, Middle, Right };

	struct _KeyLParam
	{
		short RepeatCount : 16;
		unsigned char ScanCode : 8;
		bool IsExtended : 1;
		bool ContextCode : 1;
		bool PreviousState : 1;
		bool TransitionState : 1;
	};

	public ref struct KeyLParam
	{
		private:

			_KeyLParam* keyLParam;

		public:

			KeyLParam( short repeatCount, unsigned char scanCode, bool isExtended, bool contextCode, bool previousState, bool transitionState )
			{
				keyLParam = new _KeyLParam();
				keyLParam->RepeatCount = repeatCount;
				keyLParam->ScanCode = scanCode;
				keyLParam->IsExtended = isExtended;
				keyLParam->ContextCode = contextCode;
				keyLParam->PreviousState = previousState;
				keyLParam->TransitionState = transitionState;
			}

			virtual ~KeyLParam() { delete keyLParam; keyLParam = NULL; }

		internal:

			operator LPARAM() { return (LPARAM)*(unsigned int *)keyLParam; }
	};

	public ref class RenderBuffer
	{
		private:

			const Awesomium::RenderBuffer* renderBuffer;

		public:

			RenderBuffer( const Awesomium::RenderBuffer* renderBuffer ) { this->renderBuffer = renderBuffer; }

			virtual ~RenderBuffer() { delete renderBuffer; renderBuffer = NULL; }

			RenderBuffer^ ReplaceBuffer( const Awesomium::RenderBuffer* renderBuffer )
			{
				if ( renderBuffer != this->renderBuffer )
				{
					//delete this->renderBuffer;
					this->renderBuffer = renderBuffer;
				}

				return this;
			}

			void CopyArea( array<unsigned char>^ destBuffer, int rowSpan, Rect^ rect )
			{
				pin_ptr<unsigned char> pinned = &destBuffer[ 0 ];

				return CopyArea( pinned, rowSpan, rect );
			}

			void CopyArea( IntPtr^ destBuffer, int rowSpan, Rect^ rect )
			{
				pin_ptr<unsigned char> pinned = (unsigned char *)destBuffer->ToPointer();

				return CopyArea( pinned, rowSpan, rect );
			}

			void CopyArea( unsigned char* pinned, int rowSpan, Rect^ rect )
			{
/*
				Awesomium::RenderBuffer destRenderBuffer(
					pinned, renderBuffer->width, renderBuffer->height, renderBuffer->rowSpan );

				Awesomium::Rect renderRect( rect->X, rect->Y, rect->Width, rect->Height );

				destRenderBuffer.copyArea( *renderBuffer, renderRect, renderRect );
*/
				int bpp = renderBuffer->rowSpan / renderBuffer->width;
				long offset = ( rect->Y * renderBuffer->rowSpan ) + ( rect->X * bpp );

				unsigned char* src = renderBuffer->buffer + offset;
				unsigned char* dest = pinned + offset;

				int rows = Math::Min( rect->Height, renderBuffer->height - rect->Y );
				int byteWidth = Math::Min( rect->Width, renderBuffer->width - rect->X ) * bpp;

				if ( byteWidth == rowSpan )
				{
					memcpy( dest, src, byteWidth * rows );
				}
				else
				{
					while ( rows-- > 0 )
					{
						memcpy( dest, src, byteWidth );
						src += rowSpan;
						dest += rowSpan;
					}
				}
			}

			int Width() { return renderBuffer->width; }
			int Height() { return renderBuffer->height; }
			int RowSpan() { return renderBuffer->rowSpan; }
	};

	public enum URLFilteringMode { UFM_BLACKLIST, UFM_WHITELIST, UFM_NONE };

	public ref class WebView
	{
		private:

			Awesomium::WebView* webView;
			WebViewListener^ listener;
			RenderBuffer^ lastRenderBuffer;

		public:

			WebView( Awesomium::WebView* webView )
			{
				this->webView = webView;
			}

			virtual ~WebView()
			{
				listener = nullptr;

				if ( webView != NULL )
				{
					webView->setListener( NULL );
					webView->destroy();

					webView = NULL;
				}
			}

			void LoadURL( String^ url )
			{
				webView->loadURL( marshal_as<std::string>( url ) );
			}

			void LoadHTML( String^ html )
			{
				webView->loadHTML( marshal_as<std::string>( html ) );
			}

			void LoadFile( String^ file )
			{
				webView->loadFile( marshal_as<std::string>( file ) );
			}

			void SetListener( WebViewListener^ webViewListener )
			{
				this->listener = webViewListener;

				webView->setListener( webViewListener != nullptr ? webViewListener : (Awesomium::WebViewListener*)NULL );
			}

			void GotoHistoryOffset( int offset ) { webView->goToHistoryOffset( offset ); }

			void ExecuteJavascript( String^ javascript )
			{
				webView->executeJavascript( marshal_as<std::string>( javascript ) );
			}

			void ExecuteJavascript( String^ javascript, String^ frameName )
			{
				webView->executeJavascript(
					marshal_as<std::string>( javascript ),
					marshal_as<std::wstring>( frameName ) );
			}

			FutureJSValue^ ExecuteJavascriptWithResult( String^ javascript )
			{
				return gcnew FutureJSValue( webView->executeJavascriptWithResult( marshal_as<std::string>( javascript ) ) );
			}

			FutureJSValue^ ExecuteJavascriptWithResult( String^ javascript, String^ frameName )
			{
				return gcnew FutureJSValue( webView->executeJavascriptWithResult(
					marshal_as<std::string>( javascript ),
					marshal_as<std::wstring>( frameName ) ) );
			}

			void CreateObject( String^ objectName )
			{
				webView->createObject( marshal_as<std::wstring>( objectName ) );
			}

			void DestroyObject( String^ objectName )
			{
				webView->destroyObject( marshal_as<std::wstring>( objectName ) );
			}

			void SetObjectProperty( String^ objectName, String^ propertyName, JSValue^ value )
			{
				webView->setObjectProperty( marshal_as<std::wstring>( objectName ), marshal_as<std::wstring>( propertyName ), value );
			}

			void SetObjectCallback( String^ objectName, String^ callbackName )
			{
				webView->setObjectCallback( marshal_as<std::wstring>( objectName ), marshal_as<std::wstring>( callbackName ) );
			}

			bool IsDirty() { return webView->isDirty(); }

			Rect^ GetDirtyBounds()
			{
				Awesomium::Rect dirtyBounds = webView->getDirtyBounds();
				Rect^ dirty = gcnew Rect();

				dirty->X = dirtyBounds.x;
				dirty->Y = dirtyBounds.y;
				dirty->Width = dirtyBounds.width;
				dirty->Height = dirtyBounds.height;

				return dirty;
			}

			RenderBuffer^ Render()
			{
				const Awesomium::RenderBuffer* rb = webView->render();

				return ( lastRenderBuffer != nullptr )
					? lastRenderBuffer->ReplaceBuffer( rb )
					: ( lastRenderBuffer = gcnew RenderBuffer( webView->render() ) );
			}

			void InjectMouseMove( int x, int y ) { webView->injectMouseMove( x, y ); }

			void InjectMouseDown( MouseButton button ) { webView->injectMouseDown( (Awesomium::MouseButton)(int)button ); }

			void InjectMouseUp( MouseButton button ) { webView->injectMouseUp( (Awesomium::MouseButton)(int)button ); }

			void InjectMouseWheel( int scrollAmount ) { webView->injectMouseWheel( scrollAmount ); }

			void InjectKeyboardEvent( IntPtr^ hWnd, int message, IntPtr^ wParam, IntPtr^ lParam )
			{
				LPARAM test = reinterpret_cast<LPARAM>( lParam->ToPointer() );

				Awesomium::WebKeyboardEvent* keyEvent = new Awesomium::WebKeyboardEvent(
					message,
					reinterpret_cast<WPARAM>( wParam->ToPointer() ),
					reinterpret_cast<LPARAM>( lParam->ToPointer() ) );

				webView->injectKeyboardEvent( *keyEvent );

				delete keyEvent;
			}

			void InjectKeyboardEvent( IntPtr^ hWnd, int message, int wParam, KeyLParam^ lParam )
			{
				Awesomium::WebKeyboardEvent* keyEvent = new Awesomium::WebKeyboardEvent(
					message,
					wParam,
					lParam );

				webView->injectKeyboardEvent( *keyEvent );

				delete keyEvent;
			}

			void Cut() { webView->cut(); }

			void Copy() { webView->copy(); }

			void Paste() { webView->paste(); }

			void SelectAll() { webView->selectAll(); }

			void SetZoom( int zoomPercent ) { webView->setZoom( zoomPercent ); }

			void ResetZoom() { webView->resetZoom(); }

			void Resize( int width, int height )
			{
				webView->resize( width, height );
			}

			void Resize( int width, int height, bool waitForRepaint )
			{
				webView->resize( width, height, waitForRepaint );
			}

			void Resize( int width, int height, bool waitForRepaint, int repaintTimeoutMs )
			{
				webView->resize( width, height, waitForRepaint, repaintTimeoutMs );
			}

			void Unfocus() { webView->unfocus(); }

			void Focus() { webView->focus(); }

			void SetTransparent( bool transparent ) { webView->setTransparent( transparent ); }

			void SetURLFilteringMode( URLFilteringMode mode ) { webView->setURLFilteringMode( (Awesomium::URLFilteringMode)mode ); }

			void AddURLFilter( String^ filter ) { webView->addURLFilter( marshal_as<std::wstring>( filter ) ); }

			void ClearAllURLFilters() { webView->clearAllURLFilters(); }
			
			void SetHeaderDefinition( String^ name, System::Collections::Generic::IDictionary<String^, String^>^ definition )
			{
			    cliext::collection_adapter<System::Collections::Generic::IDictionary<String^, String^>> dictAdapter( definition );
				Awesomium::HeaderDefinition& def = *new Awesomium::HeaderDefinition();

				for each ( System::Collections::Generic::KeyValuePair<String^, String^>^ kvp in dictAdapter )
				{
					std::pair<std::string, std::string> aPair( marshal_as<std::string>( kvp->Key ), marshal_as<std::string>( kvp->Value ) );
					def.insert( aPair );
				}

				webView->setHeaderDefinition( marshal_as<std::string>( name ), def );
			}

			void AddHeaderRewriteRule( String^ rule, String^ name) { webView->addHeaderRewriteRule( marshal_as<std::wstring>( rule ), marshal_as<std::string>( name ) ); }

			void RemoveHeaderRewriteRule( String^ rule ) { webView->removeHeaderRewriteRule( marshal_as<std::wstring>( rule ) ); }

			void RemoveHeaderRewriteRulesByDefinitionName( String^ name ) { webView->removeHeaderRewriteRulesByDefinitionName( marshal_as<std::string>( name ) ); }

		internal:

			operator Awesomium::WebView*() { return webView; }
	};

	public ref class WebCoreConfig
	{
		private:

			Awesomium::WebCoreConfig* webCoreConfig;

		public:

			WebCoreConfig()
			{
				this->webCoreConfig = new Awesomium::WebCoreConfig();
			}

			WebCoreConfig( Awesomium::WebCoreConfig* webCoreConfig )
			{
				this->webCoreConfig = webCoreConfig;
			}

			virtual ~WebCoreConfig() { delete webCoreConfig; webCoreConfig = NULL; }

			void SetEnablePlugins( bool enablePlugins )
			{
				webCoreConfig->setEnablePlugins( enablePlugins );
			}

			void SetUserDataPath( String^ userDataPath )
			{
				webCoreConfig->setUserDataPath( marshal_as<std::wstring>( userDataPath ) );
			}

			void SetPluginPath( String^ pluginPath )
			{
				webCoreConfig->setPluginPath( marshal_as<std::wstring>( pluginPath ) );
			}

			void SetLogPath( String^ logPath )
			{
				webCoreConfig->setLogPath( marshal_as<std::wstring>( logPath ) );
			}

			void SetLogLevel( Awesomium::LogLevel logLevel )
			{
				webCoreConfig->setLogLevel( logLevel );
			}

			void SetUserAgentOverride( String^ userAgentOverride )
			{
				webCoreConfig->setUserAgentOverride( marshal_as<std::string>( userAgentOverride ) );
			}

			void SetProxyServer( String^ proxyServer )
			{
				webCoreConfig->setProxyServer( marshal_as<std::string>( proxyServer ) );
			}

			void SetProxyConfigScript( String^ proxyConfigScript )
			{
				webCoreConfig->setProxyConfigScript( marshal_as<std::string>( proxyConfigScript ) );
			}

			void SetSaveCacheAndCookies( bool saveCacheAndCookies )
			{
				webCoreConfig->setSaveCacheAndCookies( saveCacheAndCookies );
			}

			void SetMaxCacheSize( int maxCacheSize )
			{
				webCoreConfig->setMaxCacheSize( maxCacheSize );
			}

			void SetDisableSameOriginPolicy( bool disableSameOriginPolicy )
			{
				webCoreConfig->setDisableSameOriginPolicy( disableSameOriginPolicy );
			}

			void SetCustomCSS( String^ customCSS )
			{
				webCoreConfig->setCustomCSS( marshal_as<std::string>( customCSS ) );
			}

			void SetCustomCSSFromFile( String^ customCSSFilePath )
			{
				webCoreConfig->setCustomCSSFromFile( marshal_as<std::string>( customCSSFilePath ) );
			}

		internal:

			operator Awesomium::WebCoreConfig*() { return webCoreConfig; }
	};

	public ref class WebCore
	{
		private:

			Awesomium::WebCore* webCore;

			void Initialize( WebCoreConfig^ webCoreConfig )
			{
				webCore = new Awesomium::WebCore( *webCoreConfig );
			}

		public:

			WebCore()
			{
				Initialize( gcnew WebCoreConfig() );
			}

			WebCore( WebCoreConfig^ webCoreConfig )
			{
				Initialize( webCoreConfig );
			}

			virtual ~WebCore() { delete webCore; webCore = NULL; }

			WebView^ CreateWebView( int width, int height )
			{
				return gcnew WebView( webCore->createWebView( width, height ) );
			}

			void Update()
			{
				webCore->update();
			}
	};
}